package com.zbmsg.server;

import java.util.List;
import java.util.Map;

import com.zbmsg.message.service.MessageService;
import com.zbmsg.utils.common.Request;
import com.zbmsg.utils.common.Response;
import com.zbmsg.utils.common.bean.GroupBean;
import com.zbmsg.utils.common.bean.MsgBean;
import com.zbmsg.utils.common.enums.MessageType;
import com.zbmsg.utils.spring.SpringUtil;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;

public class ZbMsgWebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
	private WebSocketServerHandshaker handshaker;

	private MessageService messageService = (MessageService) SpringUtil.getApplicationContext().getBean("messageService");

	@Override
	protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
		/**
		 * webSocket在第一次请求需要使用http请求从而完成握手操作。
		 */
		if (msg instanceof FullHttpRequest) {
			handleHttpRequest(ctx, ((FullHttpRequest) msg));
		} else if (msg instanceof WebSocketFrame) {
			System.out.println(handshaker.uri());
			handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
		}
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	@Override // 主动断开
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		System.out.println("主动断开");
		/**
		 * 现在的问题:
		 * webSocket连接成功后，前端js逻辑判断问题，导致每刷新一次页面都会主动断开，然后重新连接。
		 * 
		 * 20180905 不捕捉刷新动作,设置退出按钮.退出算退出  加入房价算加入
		 */
//		User user = this.userService.getUserByChannel(ctx.channel());// 根据channe反向查询user
//		if (user == null) {
//			return;
//		}
//		this.userService.remove(user.getId());// user销毁
//		// 如果有房间进行通知
//		if (StringUtils.isEmpty(user.getGroupId())) {
//			return;
//		}
//		this.groupService.clean(user.getGroupId());// 处理房间问题
//		ChannelGroup group = Global.groups.get(user.getGroupId());
//		if (group == null || group.size() == 0) {
//			return;// 已无房间
//		}
//		// 通知谁谁谁下线
//		MessageResult result = new MessageResult();
//		SystemBean systemBean = new SystemBean();
//		systemBean.setMessage(user.getName() + "已离线");
//		result.setSystemBean(systemBean);
//		result.setTime(TimeUtils.getCurrentTime());
//		result.setMessageType(MessageType.SYSTEMMSG_OUTGROUP.toInt());
//		group.writeAndFlush(new TextWebSocketFrame(Response.ok(result)));
		/**
		 * 登出问题:
		 * 1.如果只退出当前房间,那么cookie过期了怎么办?cookie过期了没有被销毁的channe怎么办？
		 * 解决方案:有关闭浏览器即清除cookie
		 */
	}

	@Override // 异常 Caught 抓住 抓住异常，当发生异常的时候，可以做一些相应的处理，比如打印日志、关闭链接
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}

	private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
		// 如果HTTP解码失败，返回HHTP异常
		if (!req.getDecoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
			sendHttpResponse(ctx, req,
					new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
			return;
		}
		// 获取url后置参数
		HttpMethod method = req.getMethod();
		String uri = req.getUri();
		QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri);
		Map<String, List<String>> parameters = queryStringDecoder.parameters();
		// System.out.println(parameters.get("request").get(0));
		System.out.println(parameters);
		if (method == HttpMethod.GET && "/webssss".equals(uri)) {
			// ....处理
			ctx.attr(AttributeKey.valueOf("type")).set("anzhuo");
		} else if (method == HttpMethod.GET && "/websocket".equals(uri)) {
			// ...处理
			ctx.attr(AttributeKey.valueOf("type")).set("live");
		}
		// 构造握手响应返回，本机测试
		WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
				"ws://" + req.headers().get(HttpHeaders.Names.HOST) + uri, null, false);
		handshaker = wsFactory.newHandshaker(req);
		if (handshaker == null) {
			WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
		} else {
			handshaker.handshake(ctx.channel(), req);
		}
	}

	private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
		// 判断是否关闭链路的指令
		if (frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}
		// 判断是否ping消息
		if (frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}
		// 本例程仅支持文本消息，不支持二进制消息
		if (!(frame instanceof TextWebSocketFrame)) {
			System.out.println("本例程仅支持文本消息，不支持二进制消息");
			throw new UnsupportedOperationException(
					String.format("%s frame types not supported", frame.getClass().getName()));
		}
		// 返回应答消息
		String request = ((TextWebSocketFrame) frame).text();
		System.out.println("服务端收到:" + request);
		
		//Socket返回内容对象
		//TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id() + "：" + request);
		// 群发
		//Global.group.writeAndFlush(tws);
		// 目标用户的通信管道.writeAndFlush("谁:说了什么什么")
		// ctx.channel().writeAndFlush(tws);
		
		Request r = Request.toRequest(request);
		try {
			if(r.getType() == MessageType.CREATEGROUP.toInt()) {
				GroupBean groupBean = r.toObject(GroupBean.class);
				Global.channelGroups.get(groupBean.getGroupId()).writeAndFlush(new TextWebSocketFrame(Response.ok(this.messageService.messageHandle(r, ctx.channel()))));
			}
			if(r.getType() == MessageType.JOINGROUP.toInt()) {
				GroupBean groupBean = r.toObject(GroupBean.class);
				Global.channelGroups.get(groupBean.getGroupId()).writeAndFlush(new TextWebSocketFrame(Response.ok(this.messageService.messageHandle(r, ctx.channel()))));
			}
			if(r.getType() == MessageType.SENDMSG.toInt()) {
				MsgBean msgBean = r.toObject(MsgBean.class);
				Global.channelGroups.get(msgBean.getGroupId()).writeAndFlush(new TextWebSocketFrame(Response.ok(this.messageService.messageHandle(r, ctx.channel()))));
			}
			//(webSocket返回请求)sendWebSocket(ctx,Response.ok(this.messageService.messageHandle(r, ctx.channel())));

		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("当前总人数:"+Global.users.size());
	}

	/**
	 * http返回
	 * @param ctx
	 * @param req
	 * @param res
	 */
	private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
		// 返回应答给客户端
		if (res.getStatus().code() != 200) {
			ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
			res.content().writeBytes(buf);
			buf.release();
		}
		// 如果是非Keep-Alive，关闭连接
		ChannelFuture f = ctx.channel().writeAndFlush(res);
		if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}
	}
	
	/**
	 * WebSocket返回
	 * @param ctx
	 * @param req
	 * @param res
	 */
	public void sendWebSocket(ChannelHandlerContext ctx,String msg) throws Exception {
		if (this.handshaker == null || ctx == null || ctx.isRemoved()) {
			throw new Exception("尚未握手成功，无法向客户端发送WebSocket消息");
		}
		ctx.channel().write(new TextWebSocketFrame(msg));
		ctx.flush();
	}
}
